/*
 * Copyright 2015 泛泛o0之辈
 *
 * Licensed under the Apache License, Version 2.0 (the "License");
 * you may not use this file except in compliance with the License.
 * You may obtain a copy of the License at
 *
 * http://www.apache.org/licenses/LICENSE-2.0
 *
 * Unless required by applicable law or agreed to in writing, software
 * distributed under the License is distributed on an "AS IS" BASIS,
 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
 * See the License for the specific language governing permissions and
 * limitations under the License.
 */
package cn.jfast.framework.web.api;

import cn.jfast.framework.base.util.ObjectUtils;
import cn.jfast.framework.base.util.StringUtils;
import cn.jfast.framework.jdbc.annotation.Transaction;
import cn.jfast.framework.log.LogFactory;
import cn.jfast.framework.log.LogType;
import cn.jfast.framework.log.Logger;
import cn.jfast.framework.web.aop.AopHandler;
import cn.jfast.framework.web.annotation.Aop;
import cn.jfast.framework.web.aop.TransactionAopHandler;

import java.lang.reflect.Method;
import java.util.ArrayList;
import java.util.Collections;
import java.util.Comparator;
import java.util.List;

/**
 * 封装Api动作参数
 */
public class Api {
	
	private Logger log = LogFactory.getLogger(LogType.JFast, Api.class);
	
    /** Api对应类对应路径 */
    private final String typeUri;
    /** Http请求方法 */
    private final HttpMethod apiReqMethod;
    /** Api对应方法对应路径 */
    private final String methodUri;
    /** Api对应缓存全路径 */
    private final String cacheUri;
    /** Api全路径 */
    private final String apiUri;
    /** Api类名 */
    private final Class<?> api;
    /** Api对应方法 */
    private final Method method;
    
    private int uriLength;
    /** Api对应方法拦截器链 */
    private final List<Class<? extends AopHandler>> methodAopList;
    /** 全局拦截器链 */
    private final List<Class<? extends AopHandler>> globalAopList;
    /** 所有截器链 */
    private List<Class<? extends AopHandler>> aopClassList = new ArrayList<Class<? extends AopHandler>>();
    /** 拦截器链集合 */
    private final List<AopHandler> aopList;

    /**
     * 实例化Api对象
     * @param typeRoute
     * @param methodRoute
     * @param api
     * @param method
     * @param apiReqMethod
     * @param methodAopList
     * @param globalAopList
     */
    @SuppressWarnings("unchecked")
	public Api(String typeRoute,
               String methodRoute,
               Class<?> api,
               Method method,
               HttpMethod apiReqMethod,
               List<Class<? extends AopHandler>> methodAopList,
               List<Class<? extends AopHandler>> globalAopList) {
        this.typeUri = typeRoute;
        this.methodUri = methodRoute;
        this.api = api;
        this.method = method;
        this.apiReqMethod = apiReqMethod;
        this.methodAopList = methodAopList;
        this.globalAopList = globalAopList;
        String commonRoute = commonPathFilter(staticPathFilter(typeRoute)+dynamicPathFilter(methodRoute));
        this.cacheUri = staticPathFilter(commonRoute)+ "$" + apiReqMethod.getMethod() + "$";
        this.apiUri = staticPathFilter(staticPathFilter(typeRoute)
                + staticPathFilter(methodRoute))
                + "$" + apiReqMethod.getMethod() + "$";
        uriLength = commonRoute.split("/").length;     
        List<String> routeList = ApiContext.cacheManager.getCache(apiReqMethod.getMethod()).get(uriLength,List.class);
        if(null == routeList){
        	routeList = new ArrayList<String>();
        	routeList.add(cacheUri);
        	ApiContext.cacheManager.getCache(apiReqMethod.getMethod()).put(uriLength, routeList);
        } else {
        	routeList.add(cacheUri);
        }
        aopList = new ArrayList<AopHandler>();
        merge();
    }

    public String getApiUri() {
        return apiUri;
    }

    public String getCacheUri() {
        return cacheUri;
    }

    public Class<?> getApi() {
        return api;
    }

    public Method getMethod() {
        return method;
    }

    public List<AopHandler> getAopList() {
        return aopList;
    }
    
    public String getTypeUri() {
		return typeUri;
	}

	public HttpMethod getApiReqMethod() {
		return apiReqMethod;
	}

	public String getMethodUri() {
		return methodUri;
	}

	public List<Class<? extends AopHandler>> getMethodAopList() {
		return methodAopList;
	}

	public List<Class<? extends AopHandler>> getGlobalAopList() {
		return globalAopList;
	}

	/**
     * 所有拦截器排序
     */
    private void merge() {
        try {
            if (method.isAnnotationPresent(Transaction.class)) //事务拦截器必须放置在最外层的位置
                aopList.add(TransactionAopHandler.class.newInstance());       
            aopClassList.addAll(globalAopList);
            aopClassList.addAll(methodAopList);
            Collections.sort(aopClassList, aopComparator());
            for (Class<? extends AopHandler> clazz : aopClassList)
                aopList.add(clazz.newInstance());
        } catch (InstantiationException e) {
            log.error("", e);
        } catch (IllegalAccessException e) {
        	log.error("", e);
        }
    }

    /**
     * 拦截器顺序比较器
     * @return
     */
    public Comparator<Class<? extends AopHandler>> aopComparator() {
        return new Comparator<Class<? extends AopHandler>>() {
            public int compare(Class<? extends AopHandler> o1,
                               Class<? extends AopHandler> o2) {
                return o1.getAnnotation(Aop.class).order()
                        - o2.getAnnotation(Aop.class).order();
            }
        };
    }

 
    protected static String staticPathFilter(String path) {
        return ("/" + path + "/").replaceAll("/+", "/");
    }
    
    protected static String commonPathFilter(String path){
    	if(path.startsWith("/"))
    		path = path.substring(1);
    	if(path.endsWith("/"))
    		path = path.substring(0, path.length()-1);
    	return path.replaceAll("/+", "/");
    }

    protected static String dynamicPathFilter(String path) {
        path = staticPathFilter(path);
        String[] dynamicPaths = StringUtils.substringsBetween(path,":","/");
        if(ObjectUtils.isEmpty(dynamicPaths)) {
            return path;
        } else {
            String[] pathTokens = path.split("/");
            path = "";
            for(String token:pathTokens){
                if(!token.startsWith(":"))
                    path += token+"/";
                else
                    path += "{}/";
            }
            return staticPathFilter(path);
        }
    }

}
